package com.chaihu.jpaDemo.common.util.convertVo;

//import com.chaihu.jpaDemo.modules.system.entity.Dict;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

//import com.chaihu.jpaDemo.common.cache.DictAllTypeCache;

/**
 * 描述:
 * 作者: qinzhw
 * 创建时间: 2018-09-28 22:33
 **/
public class ConverterVoUtils {
    static Logger logger = LoggerFactory.getLogger(ConverterVoUtils.class);
    /**
     * 翻译当前类中需要翻译的字典值
     * @param source 待翻译的对象
     */
    public static <T> void dataFormatter(T source) {

        //判断原对象是否为null
        Assert.notNull(source, "待翻译的原对象不能为null");

        //获取所有属性并翻译字典
        Field[] declaredFields = source.getClass().getDeclaredFields();
        //翻译字典：找出所有含有@CacheFormatter的属性
        Stream<Field> fieldStream = Arrays.stream(declaredFields)
                //排除没有注解@CacheFormatter的字段
                .filter(field -> field.isAnnotationPresent(CacheFormat.class));
        //翻译
        doFormatter(fieldStream, source, source.getClass());
    }

    /**
     * 翻译当前集合类中需要翻译的字典值
     * @param sources 待翻译的集合对象
     */
    public static <T> void dataFormatter(List<T> sources) {

        //当翻译的集合为空时,返回空的集合
        if (sources == null || sources.isEmpty()) {
            return;
        }

        Class targetClass = sources.get(0).getClass();
        //获取所有属性并翻译字典
        Field[] declaredFields = targetClass.getDeclaredFields();
        //翻译字典：找出所有含有@CacheFormat的属性集合
        List<Field> formatterFields = Arrays.stream(declaredFields)
                //排除没有注解@CacheFormat的字段
                .filter(field -> field.isAnnotationPresent(CacheFormat.class))
                .collect(Collectors.toList());
        //循环列表（并行操作 parallelStream 不安全）
        sources.parallelStream().forEach(target -> {
            //翻译
            doFormatter(formatterFields.stream(), target, targetClass);
        });
    }



    /**
     * 对目标类需要翻译的字段进行翻译
     *
     * @param stream
     * @param target      目标对象
     * @param targetClass 目标对象类
     */
    private static <T> void doFormatter(Stream<Field> stream, Object target, Class<T> targetClass) {

//        DictAllTypeCache dictAllTypeCache = SpringUtil.getBean(DictAllTypeCache.class);

        //排除目标对象中字段值为null的字段
        stream.filter(field -> {
            PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, field.getName());
            Object invoke = null;
            try {
                invoke = propertyDescriptor.getReadMethod().invoke(target, new Object[]{});
            } catch (IllegalAccessException e) {
                logger.warn("待翻译的字段的get是无法访问的", e);
            } catch (InvocationTargetException e) {
                logger.warn("调用待翻译的字段的get方法时报错", e);
            } catch (Exception e) {
                logger.warn("确保属性有get,set方法", e);
            }
            return invoke != null;
            //遍历需要翻译的字段
        }).forEach(field -> {
            CacheFormat annotation = field.getAnnotation(CacheFormat.class);

            //缓存系统编号，如果不指定则默认为当前系统编号
            String systemCode = "system_code";
            if (StringUtils.isNotBlank(annotation.systemCode())) {
                systemCode = annotation.systemCode();
            }
            //缓存key，如果不指定，则默认为字段名称
            String key = annotation.key();
            if (StringUtils.isBlank(key)) {
                key = field.getName();
            }

            //判断注解@CacheFormatter是否指定把字典翻译到另一个字段上
            String formatterField = annotation.destination();
            if (StringUtils.isBlank(formatterField)) {
                //当注解中不指定其他字段时，默认翻译到加注解的属性上
                formatterField = field.getName();
            }

            try {
                PropertyDescriptor orginPropertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, field.getName());
                Object value = orginPropertyDescriptor.getReadMethod().invoke(target, new Object[]{});
                //设置目标字段值
                PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, formatterField);
                //取缓存
                /*Dict dict = dictAllTypeCache.findDictByCode(Integer.valueOf(value.toString()));
                if (dict != null) {
                    //设置缓存值到属性字段中
                    propertyDescriptor.getWriteMethod().invoke(target, dict.getName());
                }else {
                    propertyDescriptor.getWriteMethod().invoke(target, value.toString());
                }*/




            } catch (IllegalAccessException e) {
                logger.warn("待翻译的字段的set是无法访问的", e);
            } catch (InvocationTargetException e) {
                logger.warn("调用待翻译的字段的set方法时报错", e);
            } catch (Exception e) {
                e.printStackTrace();
                logger.warn("调用待翻译的字段的set方法时报错,推测类型不匹配", e);
            }
        });
    }


    /*************************转换************************/

    /**
     * 把原对象转换成目标类的对象，并翻译目标类的属性字典
     * 只针对目标类没有范型或者范型与原对象一样
     * @param source      原对象
     * @param targetClass 目标类
     * @return 目标对象
     */
    public static <T> T dataConvert(Object source, Class<T> targetClass) {
        Assert.notNull(source, "原对象不能为null");
        Assert.notNull(targetClass, "目标class不能为null");
        T target = BeanUtils.instantiateClass(targetClass);
        //把目标对象的属性设置成原对象中对应的属性
        try {
            BeanUtils.copyProperties(source, target);
        } catch (Exception e) {
            e.printStackTrace();
        }

        dataFormatter(target);
        return target;
    }

    /**
     * 实体属性互转
     *
     * @param source 原对象
     * @param target 目标对象
     * @return 目标对象
     */
    public static <T> T dataObjConvert(Object source, T target) {
        Assert.notNull(source, "待转换的原对象不能为null");
        Assert.notNull(target, "目标对象不能为null");

        //转换
        try {
            BeanUtils.copyProperties(source, target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //翻译
        dataFormatter(target);
        return target;
    }


    /**
     * 批量把原对象转换成目标对象，并翻译目标对象的属性字典
     * 如果想返回指定类型的集合即List的子类,参考
     *
     * @param sources     原对象集合
     * @param targetClass 目标对象的类
     * @return 返回转换后的目标集合
     */
    public static <T, E> List<T> dataConverts(List<E> sources, Class<T> targetClass) {

        try {
            if (targetClass == null) {
                new RuntimeException("转换的目标Class不能为null");
            }
            //当翻译的集合为空时,返回空的集合
            if (sources == null || sources.isEmpty()) {
                List<T> targetList = new ArrayList<>();
                return targetList;
            }
            //获取原集合的类型
            Class<? extends List> aClass = sources.getClass();
            //目标集合
            List<T> targetList = BeanUtils.instantiateClass(aClass);

            //把目标对象的属性设置成原对象中对应的属性（并行操作sources.parallelStream().forEach 不安全）
            sources.forEach(item -> {
                T target = BeanUtils.instantiateClass(targetClass);
                BeanUtils.copyProperties(item, target);
                targetList.add(target);
            });

            //翻译字典
            dataFormatter(targetList);

            return targetList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 返回指定类型的方法,这里的类型必须是List的子类
     * 批量把原对象转换成目标对象，并翻译目标对象的属性字典,
     *
     * @param sources     原对象集合
     * @param targetClass 目标对象的类
     * @param returnType  返回值类型
     * @return 返回转换后的目标集合
     */
    public static <T, E, R extends List<T>> R dataConverts2(List<E> sources, Class<T> targetClass, Class<R> returnType) {

        Assert.notNull(targetClass, "转换的目标Class不能为null");
        Assert.notNull(returnType, "返回值类型Class不能为null");

        //当翻译的集合为空时,返回空的集合
        if (sources == null || sources.isEmpty()) {
            return null;
        }
        //目标集合
        R targetList = BeanUtils.instantiateClass(returnType);

        //把目标对象的属性设置成原对象中对应的属性（并行操作sources.parallelStream().forEach 不安全）
        sources.forEach(item -> {
            T target = BeanUtils.instantiateClass(targetClass);
            BeanUtils.copyProperties(item, target);
            targetList.add(target);
        });

        //翻译字典
        dataFormatter(targetList);
        return targetList;
    }

}
